今天我們將實作當使用者成功登入後看到的頁面。我們會實作一個底部導覽列進行頁面的切換。效果如下:
讓我們開始吧!
Cupertino 提供了一個好用的底部導覽列的元件 CupertinoTabScaffold
,它實作了 iOS 風格的底部導覽列設計。
在引用之前可以先仔細想一下頁面之間的階層關係,我們的新聞 APP 即將製作四個頁面分別為:
這四個頁面之間並沒有明顯的階層關係,因此使用底部導覽列的設計相當合理。可以看作四個為各自不同的分頁。也因此,在 CupertinoTabScaffold
中這四者的 Navigator
是各自分開的,也就是有四個不同的 Stack
來管理路由的狀態。
同樣的在使用 widget 前,我們先來看看類別定義:
CupertinoTabScaffold({
super.key,
required this.tabBar, // 顯示在底部的導覽列
required this.tabBuilder, // 根據導覽列的狀態建構上方的頁面內容
this.controller,
this.backgroundColor,
this.resizeToAvoidBottomInset = true,
this.restorationId,
})
從類別定義中,我們可以知道我們至少需要提供 tabBar
與 tabBuilder
。請在 screens
資料夾底部新增一個 tab_layout.dart
的檔案,並參考下列程式碼:
// 記得要把 main.dart 的 home 參數改為顯示 Tablayout()
class TabLayout extends StatelessWidget {
const TabLayout({super.key});
@override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.house_fill, size: 24), label: '主頁'),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.square_grid_2x2_fill, size: 24),
label: '探索'),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.search, size: 24), label: '搜尋'),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.person_crop_circle, size: 24),
label: '個人檔案'),
]),
tabBuilder: (BuildContext context, int index) =>
CupertinoPageScaffold(
child: Center(
child: Text('Tab $index'),
),
));
}
}
上述程式碼我們在 tabBar
參數串接了由 BottomnavigationItem
組成的 List
,並且每個元素皆有給定指定的 Icon
與分頁名稱。tabBuilder
則是顯示當前的 tab 的索引值。
圖標使用的是 flutter 預載的 cupertino_icons
套件所提供,引用方法可參考上方程式碼。也可到這個網站檢視所有的 Icons,並自行任意的替換。
這時候你應該會發現底部的導覽列(藍色)好像顏色跟設計稿的顏色不一樣(紅色)
當我們在設計一款應用或是網頁時,通常都會決定好一個顏色作為主色 (primary color),原因無他,就是為了讓整體更具一體性。
因此我們有必要在一開始就設定好應用程式的主題。在 Cupertino 中有提供 CupertinoThemeData
的類別專門用於進行這類的設定,透過類別定義了來看看我們能設定些什麼屬性:
const CupertinoThemeData({
Brightness? brightness, // 亮色與暗色模式
Color? primaryColor, // 設定主色
Color? primaryContrastingColor, // 與主色對比的顏色
CupertinoTextThemeData? textTheme, // 定義文本的樣式
Color? barBackgroundColor, // 導航列的顏色
Color? scaffoldBackgroundColor, // 骨架的背景顏色
bool? applyThemeToAll, // 是否自動套用至所有 Cupertino 的 widget
})
因此我們可以從此部分針對文字、主色來進行設定。請打開 main.dart
並在 CupertinoApp
中添加程式碼,我們將為整個應用程式進行主題設定:
CupertinoApp(
// 上方省略
theme: CupertinoThemeData(
// 文字相關的主題設定
textTheme: CupertinoTextThemeData(
// 大標題樣式設定
navLargeTitleTextStyle: TextStyle(
fontSize: 34,
fontWeight: FontWeight.w700,
color: CupertinoColors.black,
),
// 一般文字樣式設定
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: CupertinoColors.black,
),
),
// 主色
primaryColor: Color.fromRGBO(255, 30, 84, 1),
// 導覽列背景顏色
barBackgroundColor: CupertinoColors.white,
),
);
加入之後便會套用設定至當前的應用程式上,此時可以發現底部的導覽列的顏色已經套用了我們設定的 primaryColor
。
目前我們離頁面還尚缺了頂部的工具列,其用於顯示當前頁面的標題、操作按鈕以及返回上頁的按鈕。
我們來仔細觀察一下原生 iOS 的 APP 的頂部工具列行為
在 Cupertino 提供了 CupertinoSliverNavigationBar
實現了相應的行為。我們將其加入至 tab_layout.dart
的程式碼中。
// 僅顯示 tabBuilder 的內容
tabBuilder: (BuildContext context, int index) =>
CupertinoPageScaffold(
child: CustomScrollView(slivers: <Widget>[
CupertinoSliverNavigationBar(
largeTitle: Text(tabTitle[index]),
backgroundColor: CupertinoColors.white),
]))
我們來一一講解上述程式碼的內容:
tabBuilder
用於建構當前 tab 的頁面。CupertinoPageScaffold
widgetCustomScrollView
CupertinoSliverNavigationBar
並設定 tab 索引值對應至正確的標題。試著操作看看當前的頁面吧!原則上可以根據不同的 tab 顯示不同的標題,並且也可以達成滑動時將當前標題縮小置中的效果。
看起來目前都相當的棒。
不過因為我個人喜歡較圓的字體XDD 所以我想在本篇的最後面來介紹如何引用外部字體。
在前面的章節我們有建立 assets
這個資料夾並用於放置圖片、字體檔案。前面章節也有介紹過要如何引入圖片並呈現於應用程式中了,其實字體也大同小異。
在選擇字體時,我們先把握一個原則。當我們在看網頁或是報章雜誌時,通常會使用字體大小以及粗細程度來階層化。
雖然這並非挑選字體的鐵則,不過請挑選至少有 5 種粗細程度的字體,在顯示效果上才能更加的豐富。
你可以透過 Google Fonts 找到你喜歡的字體,並透過 google_fonts
這個套件進行引入。關於套件使用方式可參考該套件的文件,我們就不花篇幅來講解。
這裡我從網路上找到了一款可免費商用的字體「源泉源體」,該款字體提供了 6 種不同的粗細程度樣式,因此我將以如何引入該字體來進行介紹。
下載字體:請前往 https://github.com/ButTaiwan/gensen-font,並點選 「Download ZIP」 按鈕下載字體的壓縮檔
解壓縮後將 ttc
當中的 6 個字體檔移至 assets/fonts
資料夾下
打開 pubspsec.yml
檔案,並輸入以下文字內容
fonts:
- family: GenSenRounded
fonts:
- asset: assets/fonts/GenSenRounded-EL.ttc
weight: 200
- asset: assets/fonts/GenSenRounded-L.ttc
weight: 300
- asset: assets/fonts/GenSenRounded-R.ttc
weight: 400
- asset: assets/fonts/GenSenRounded-M.ttc
weight: 500
- asset: assets/fonts/GenSenRounded-B.ttc
weight: 700
- asset: assets/fonts/GenSenRounded-H.ttc
weight: 900
我們針對這個字體分別使不同的 weight 對應至不同的字體樣式檔
實際使用:請打開 main.dart
中方才新增的 textTheme
程式碼,加入字體
textTheme: CupertinoTextThemeData(
navLargeTitleTextStyle: TextStyle(
fontSize: 34,
fontWeight: FontWeight.w700, // 查上方的表,套用 GenSenRounded-B.ttc
fontFamily: 'GenSenRounded', // 套用我們方才引用的字體
color: CupertinoColors.black,
letterSpacing: 1.05),
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400, // 查上方的表,套用 GenSenRounded-R.ttc
fontFamily: 'GenSenRounded', // 套用我們方才引用的字體
color: CupertinoColors.black,
),
),
引用成功拉
今天我們根據應用程式的需求建構了底部的導航列,也加入了頂部的工具列讓整個程式碼的外框更加完整。同時我們也套用了主題、字形樣式以及引入了網路上的開源字體。
我想我們可以認同 Flutter 所提供的這套 Cupertino 設計工具真的很強大,不僅在動畫、效果上都非常接近原生的 iOS 應用程式。不說我還以為是用 SwiftUI 開發出來的介面呢 XDD
明天開始會開始開發「探索頁面」的部分,我們將會介紹兩個在 Flutter 中用來顯示列表的重要組件 ListView
與 GridView
,我們明天繼續努力!
今天的參考程式碼:https://github.com/ChungHanLin/micro_news_tutorial/tree/day14/micro_news_app